
import c3d from '../cc';
import { TaskStart } from './TaskStart';

/**
 *  待完善
 *  自动录制
 *  添加其他命令
 *  
 *  已完成： 正常流程引导，引导存储
 */
const { ccclass, property, executeInEditMode } = c3d._decorator;


/**命令，字符对应Mgr里的方法 */
export enum CMD {
    /**手指命令 */
    MOVEFINGER = `movefinger`,
    /**对话框,暂未实现 */
    // DIALOGUE = 'dialogue',
    /**拖拽节点命令,暂未实现 */
    // DRAGNODE = `dragNode`,
}

/**遮罩类型 */
export enum MaskType {
    /**矩形遮罩 */
    rect = `rect`,
    /**圆形遮罩 */
    cicular = `cicular`,
}

/**
 *  自定义参数
 *  各个参数的优先级， step > task ， 默认每个step覆盖task*/
export interface I_TaskBaseParme {
    /**是否自动执行该步骤,默认不自动执行*/
    autoRun?: boolean;
    /**自动执行点击指令时的点击时间 , 默认1s*/
    autoClickTime?: number;
    /**步骤完成是否自动存储 ,默认不存储 */
    autoSave?: boolean;

    /**节点查找时间,默认为2s */
    nodeFindTime?: number;
    /*节点查找次数，默认为20次，-1为一直查找 */
    nodeFindCount?: number;
    /**开始延迟时间  默认0*/
    startDelayTime?: number;
    /**结束延迟时间 默认0 */
    endDelayTime?: number;

    /**引导步骤结束后是否关闭黑色遮罩, 默认关闭 */
    finishCloseMask?: boolean;
    /**引导步骤结束后是否关闭点击拦截，默认开启 */
    finishCloseClick?: boolean;

    /**遮罩类型 ,默认矩形*/
    maskType?: MaskType;
    /**遮罩设为矩形类型时遮罩圆角大小， 圆形类型时是圆形的半径，默认0  */
    maskFillet?: number;
    /**遮罩设为矩形类型时，设置的遮罩虚化度， 默认0 */
    maskVirtualization?: number;
    /**播放遮罩动画时间 , 默认时间 0.3*/
    maskAnimTime?: number;
    /**是否播放遮罩动画, 默认不播放 */
    maskAnim?: boolean;

    /**引导手指是否沿着x轴偏转，默认不偏转 */
    fingerDeflectionX?: boolean;
    /**引导手指是否沿着y轴偏转，默认不偏转 */
    fingerDeflectionY?: boolean;

    /**气泡是否在右边*/
    bubbleIsRight?: boolean;
    /**气泡是否在上面 */
    bubbleIsUp?: boolean;
}

/**任务配置 */
export interface I_Task extends I_TaskBaseParme {
    /**任务名字 */
    taskName: string;
    /**任务步骤 */
    steps: I_TaskStep[];
    /**调试 */
    debug?: boolean;
    /**默认开始引导,如果默认不填写的话默认从第一个开始 */
    startStep?: number;
    /**任务开始,自定义执行配置不执行，只有模块化任务执行 */
    onTaskStart?(): void | Promise<void>;
    /**任务结束,自定义执行配置不执行，只有模块化任务执行 */
    onTaskEnd?(): void | Promise<void>;
}

/**任务步骤配置 */
export interface I_TaskStep extends I_TaskBaseParme {
    /**引导序列存储key序列*/
    stepIndex: number;
    /**步骤名字id用于存储进度key的前缀*/
    name: string;
    /**指令 */
    command: {
        /**指令 */
        cmd: CMD;
        /**引导节点参数，使用注册器注册的名字/canvas下节点路径 */
        findArg: string;
        /**引导文本 */
        text?: string;
    },

    /**引导开始 */
    onStepStart?(): void | Promise<void>;
    /**手指移动开始 */
    onStepExcute?(): void | Promise<void>;
    /**点击后 */
    onStepEnd?(): void | Promise<void>;
}
/**节点注册器 */
export interface I_Registere {
    /**要注册的引导框节点 */
    registerNode: c3d.Node;
    /**注册的名字 */
    registerName: string;
    /**文本框位置 */
    registerTextNode: c3d.Node;
}
/**引导启动配置 */
export interface I_TaskStart {
    /**引导启动名 */
    taskFlieName?: string;
    /**引导id */
    stepIndex?: number;
}

interface I_UIGuide { }

const RADIAN = 2 * Math.PI / 360;

@ccclass
@executeInEditMode
export class GuideTaskMgr extends c3d.Component {
    @property({ displayName: `引导任务列表`, type: TaskStart })
    taskList: TaskStart = null;

    /**控制mask，手指和特效圈 等的根节点*/
    @property({ displayName: `引导节点`, type: c3d.Node })
    guideLayer: c3d.Node = null;

    @property({ displayName: `全屏屏蔽节点`, type: c3d.Node })
    notTouch: c3d.Node = null;

    // @property({ displayName: `mask遮罩控制`, type: HollowOut })
    mask: any = null;

    @property({ displayName: `特效层`, type: c3d.Node })
    guideTx: c3d.Node = null;

    @property({ displayName: `引导手指`, type: c3d.Node })
    guideFinger: c3d.Node = null;

    @property({ displayName: `引导文本根节点`, type: c3d.Node })
    guideTextRoot: c3d.Node = null;
    @property({ displayName: `引导文本组件`, type: c3d.Label })
    guideTextCom: c3d.Label = null;
    @property({ displayName: `气泡背景`, type: c3d.Node })
    popBg: c3d.Node = null;

    /**引导的目标（也就是要引导玩家操作的目标） */
    _targetNode: c3d.Node;
    /**当前启动引导配置 */
    _taskConfig: I_TaskStart;
    /**当前引导配置文件 */
    _taskFile: I_Task;
    /**当前引导步骤 */
    _taskStep: I_TaskStep;

    /**注册的节点池 */
    _nodeMap: Map<string, I_Registere> = new Map();
    /**所有引导stepMap */
    _stepMap: Map<string, boolean>;

    /**是否处在引导中 */
    _isGuiding = false;
    /** 是否打开调试面板*/
    // _isOpenMaskInfo: boolean = true;

    /**是否加载了引导 */
    _isLoad = false;

    onLoad() {
        // if (c3d_EDITOR && !this.mask) {
        //     //底图
        //     let maskBgNode = new c3d.Node('MaskBg');
        //     let maskSp = maskBgNode.addComponent(c3d.Sprite);
        //     //加载默认图片
        //     c3d.assetManager.loadAny(`a23235d1-15db-4b95-8439-a2e005bfff91`, (err, sp) => {
        //         //@ts-ignore
        //         if (err) return Editor.error(err);
        //         maskSp.sizeMode = c3d.Sprite.SizeMode.CUSTOM;
        //         maskSp.spriteFrame = sp;
        //         maskBgNode.color = c3d.Color.BLACK;
        //         maskBgNode.opacity = 180;
        //         maskBgNode.setContentSize(c3d.size(2500, 2500));
        //         //创建mask并添加到场景
        //         let mask = new c3d.Node(`mask`).addComponent(c3d.Mask);
        //         mask.inverted = true;
        //         mask.node.setContentSize(c3d.size(2500, 2500));
        //         mask.node.addChild(maskBgNode);
        //         this.node.addChild(mask.node);
        //         // this.mask = mask;
        //     });
        // }
    }

    init() {
        //设置节点大小
        // this.node.setContentSize(c3d.winSize);
        //Registered 点击层事件监听
        this.node.on(c3d.Node.EventType.TOUCH_START, this.setSwallowTouchesEvent, this);
        //mask遮挡面板默认不开启，只有在引导时在开启
        this.mask.node.active = false;
        this.guideLayer.active = false;
        this.guideFinger.active = false;
        this.guideTx.active = false;
        this.guideTextRoot.active = false;
        this.notTouch.active = false;
        this._isLoad = true;

        //获取引导数据
        if (!this._stepMap)
            this._stepMap = this.getAllStepData();
    }

    /**
     * 注册要观测引导的节点信息
     * @param data 注册信息
     */
    NodeRegistered(data: I_Registere) {
        c3d.log(`要注册的引导节点:${data.registerName}`);
        if (this._nodeMap.has(data.registerName))
            c3d.warn(`已注册引导节点:${data.registerName}`);
        this._nodeMap.set(data.registerName, data);
    }

    //#region  引导配置入口

    /**
     * 根据自定义初始引导器进行单步的组合引导（不会进行配置文件的onTaskStart等周期函数）
     * @param startTaskConfig 引导启动配置
     * @param taskFileName 默认引导的文件名字
     * @example
     *  let evtData = { stepIndex: 0 }
     *  ...doTaskStart
     *  await this.mgr.ExcuteGuideTask([evtData, evtData], "task1");
     *  ...doTaskEnd
     */
    async ExcuteGuideTask(startTaskConfig: I_TaskStart[], taskFileName = "") {
        if (this.checkIsGuiding(`不能再次启动自定义引导`) && !this._isLoad) return;
        if (this._stepMap.has(taskFileName)) return;

        this._taskStart(`【执行自定义引导步骤】：`, startTaskConfig);
        for (let data of startTaskConfig) {
            c3d.log("【自定义引导步骤完成】:接受的引导数据:"), c3d.log(data);
            this._taskConfig = data;

            let task = this.getTaskFileData(data, taskFileName);
            if (task)
                await this.ExcuteGuideStep(task, data.stepIndex);
            else
                c3d.warn(`没有自定义引导数据：${taskFileName}`);
        }
        this._taskEnd(`【自定义引导步骤完成】`);
    }

    /**
     * 根据引导文件名执行一个单元模块(整个文件)
     * @param taskFielName 执行文件名
     * @example
     *   有一个task.ts 文件,将完整执行这个文件的所有引导步骤
     *   ExecutiveGuideUnitStr("task");
     */
    async ExecutiveGuideUnitStr(taskFielName: string) {
        if (this.checkIsGuiding(`不能执行引导模块`) && !this._isLoad) return;
        if (this._stepMap.has(taskFielName)) return;

        let task = this.getTaskFileData({}, taskFielName);
        if (task) {
            c3d.log(`【执行引导文件名${taskFielName}】：开始`);
            await this.ExecutiveGuideUnitData(task);
            c3d.log(`【引导文件名${taskFielName}】：完成`);
        } else
            c3d.warn(`未查找到引导文件:${taskFielName}`);
    }

    /**
     * 根据引导模块数据执行一个单元模块(整个文件)
     * @param task 引导数据
     */
    async ExecutiveGuideUnitData(task: I_Task) {
        if (!this._isLoad || !task) return;
        if (this._stepMap.has(task?.taskName)) return;

        this._taskStart(`【执行引导模块】：${task.taskName}`, task);
        await task.onTaskStart?.();
        for (let index of Object.keys(task?.steps ?? {}))
            await this.ExcuteGuideStep(task, parseInt(index));
        await task.onTaskEnd?.();

        this._taskEnd(`【引导模块完成】：${task.taskName}`);
    }

    //#endregion 

    /**
     * [核心]执行step引导
     * @param task 任务配置
     * @param stepId 要执行的step
     */
    async ExcuteGuideStep(task: I_Task, stepId: number) {
        this._taskFile = task;
        let step = task?.steps[stepId];
        let startStep = task.startStep ?? 0;
        // if (GM.main.appDebug.isJumpGuide) return;

        if (step && stepId >= startStep) {
            step.name = step.name + step.stepIndex;

            //检查是否已经完成了该引导
            if (this.getStepSchedule(step.name)) {
                c3d.log(`引导：${step.name}， id: ${stepId}已经完成`);
                return;
            }
            c3d.log(`开始引导步骤:${step.name},stepId:${stepId}`);

            //每次引导执行前，都将之前的引导目标清空
            this._targetNode = null;
            this._taskStep = step;

            //开始回调
            await step.onStepStart?.();
            // await Tool.promise.delay(step.startDelayTime ?? task.startDelayTime);

            //执行命令
            await this.executeCommand(step.command.cmd);
            //判断是否需要保存该步骤的引导
            this.saveStep(step);

            //处理引导mask.手指,点击
            let isCloseMask = step.finishCloseMask ?? task.finishCloseMask ?? true;
            let isCloseTouch = step.finishCloseClick ?? task.finishCloseClick ?? false;
            this._finishClear(isCloseMask, isCloseTouch);

            //结束回调
            // await Tool.promise.delay(step.endDelayTime ?? task.endDelayTime);
            await step.onStepEnd?.();
        } else
            c3d.warn(`未查找到引导步骤:${task},id:${stepId}`);
        c3d.log(`引导步骤:${step.name},stepId:${stepId}，完成`);
    }

    /**
     * 根据初始引导器获取引导配置数据
     * @param data 开始引导数据
     * @param taskFileName 默认引导配置名字
     * @returns task
     */
    getTaskFileData(data: I_TaskStart, taskFileName = ''): I_Task {
        let fileName = data?.taskFlieName ?? taskFileName;
        let task = (require(fileName) as any)?.default;
        if (!task)
            return;
        return new task();
    }

    /**
     * 引导开始时候执行
     * @param logStr 日志内容
     * @param parme 引导数据
     */
    _taskStart(logStr: string, parme?: any) {
        this._finishClear();
        this.guideLayer.active = true;
        this._isGuiding = true;
        this.notTouch.active = true;

        c3d.log(logStr);
        parme && c3d.log(parme);
    }

    /**
     * 
     * @param log 日志内容
     */
    _taskEnd(logStr: string) {
        this._finishClear();
        this._isGuiding = false;
        this.guideFinger.active = false;
        this.guideTextRoot.active = false;
        this.guideTx.active = false;
        this.guideLayer.active = false;
        this.notTouch.active = false;

        //存储任务
        this.saveTask(this._taskFile?.taskName);
        c3d.log(logStr);
    }

    /**
     * 完成清理
     * @param clearMaskGraphics 是否关闭mask 默认关闭
     * @param clearMask 是否关闭点击层 默认关闭
     */
    _finishClear(clearMaskGraphics = true, clearMask = true) {
        this.mask.node.opacity = clearMaskGraphics ? 0 : 180;
        this.mask.node.active = !clearMask;
    }

    /**
     * 事件的吞没处理机制
     * @param event 事件
     * @returns 
     */

    setSwallowTouchesEvent(event: c3d.Touch) {
        if (!this.mask.node.active) {
            this.setSwallowTouches(this.node, false);
            return;
        }
        if (!this._targetNode) {
            this.setSwallowTouches(this.node, true);
            return;
        }
        if (!c3d.isValid(this._targetNode)) {
            return;
        }
        //如果玩家点击了规定的区域，则让事件往下派发
        // let rect = this._targetNode.getBoundingBoxToWorld(); //转换到世界矩形后尺寸不对
        // let v = this._targetNode.getBoundingBox();
        // rect.width = v.width;
        // rect.height = v.height;
        // let isContains = rect.contains(event.getLocation());
        // this.setSwallowTouches(this.node, !isContains);

        //tets
        //c3d.log(isContains)
        // let xmin = rect.xMin;
        // let xmax = rect.xMax;
        // let ymin = rect.yMin;
        // let ymax = rect.yMax;
        // let arr = [c3d.v2(xmin, ymin), c3d.v2(xmax, ymin), c3d.v2(xmax, ymax), c3d.v2(xmin, ymax)];
        // isContains = c3d.Intersection.pointInPolygon(event.getLocation(), arr);
    }

    /**
     * 设置节点是否吞噬事件
     * @param node 节点
     * @param isSwallow 是否吞噬
     */
    setSwallowTouches(node: c3d.Node, isSwallow = false) {
        let n = node as c3d.Node & { _touchListener: any }
        n._touchListener.setSwallowTouches(isSwallow);
        // 如果设置穿透，监听touchStart事件必须开启，不然事件会被过滤
        // c3d.macro.ENABLE_MULTI_TOUCH = !isSwallow;  //如果是mask组件来做遮罩，并且关闭了多点触控，就必须设置，否则无法穿透
    }

    /**
     *  获取遮罩的graphics
     * @returns mask._graphics
     */
    get getMaskGraphics() {
        return this?.mask?.getComponent(c3d.Sprite)?.[`_graphics`] as c3d.Graphics;
    }

    /**
     * 隐藏遮罩，并返回一个显示遮罩的回调
     * @returns 显示遮罩回调
     */
    hideMask() {
        this._finishClear();
        this.notTouch.active = false;
        return () => {
            this._finishClear(false, false);
            this.notTouch.active = true;
        };
    }

    /**
     * 检查当前是否正在进行引导
     * @param str 提示字符
     * @returns 
     */
    checkIsGuiding(str = '...') {
        this._isGuiding && c3d.warn(`当前正在引导中${str}`);
        return this._isGuiding;
    }

    //#region  引导数据

    private _dataKey = `GudeTaskStepData`;
    /**
     * 保存指定的step
     * @param step 引导step
     */
    saveStep(step: I_TaskStep) {
        let isSave = step.autoSave ?? this._taskFile.autoSave ?? false;
        if (isSave) {
            if (this._stepMap.has(step.name)) {
                c3d.warn(`引导步骤:${step.name}已经保存过,现已被覆盖！`);
                return;
            }
            this._stepMap.set(step.name, true);
            this.saveAll();
        }
    }

    /**
     * 保存引导任务
     * @param name 引导任务名字
     */
    saveTask(name: string) {
        if (name && name != '') {
            this._stepMap.set(name, true);
            this.saveAll();
        }
    }

    /**
     * 获取step引导的进度
     * @param name step引导的名字
     */
    getStepSchedule(name: string) {
        //加载引导数据
        return this._stepMap.get(name) ?? false;
    }

    /** 立即存储现在的引导数据  */
    saveAll() {
        c3d.sys.localStorage.setItem(this._dataKey, JSON.stringify(Object.fromEntries(this._stepMap)));
    }

    /**  * 获取所有存储的引导数据  */
    getAllStepData() {
        let data = c3d.sys.localStorage.getItem(this._dataKey);
        // data = data ? new Map(Object.entries(JSON.parse(data))) : new Map();
        return data as unknown as this[`_stepMap`];
    }

    /**  * 清除所有的引导数据  */
    clearAllStep() {
        this._stepMap.clear();
        c3d.sys.localStorage.removeItem(this._dataKey);
    }

    //#endregion

    /*********************命令************************* */

    /**
     * 执行引导命令
     * @param command 引导命令
     * @returns 
     */
    executeCommand(command: CMD) {
        c3d.log(`执行引导命令:${command}`);
        return this[command]?.();
    }

    /**
     * 根据step移动手指到节点
     * @returns promise
     */
    async movefinger() {
        let commandData = this._taskStep.command;
        let nodeName = commandData.findArg;
        let targetNode: c3d.Node = null;
        let curFindCount = 0;
        //查找节点
        do {
            //尝试获取注册的节点，如果没有将使用路径查找
            targetNode = this._nodeMap.get(nodeName)?.registerNode;
            targetNode = targetNode ?? await this.find(nodeName);
            if (!targetNode) {
                let delayTime = this._taskStep.nodeFindTime ?? this._taskFile.nodeFindTime ?? 2;
                let findCount = this._taskStep.nodeFindCount ?? this._taskFile.nodeFindCount ?? 20;
                let lastCount = findCount - curFindCount;
                //超过指定次数跳过该引导
                if (lastCount <= 0 && findCount != -1) {
                    c3d.warn(`获取引导节点：${nodeName}失败，到到查找次数：${findCount}, 跳过引导:${this._taskStep.name},查找节点：${nodeName}`);
                    return;
                } else {
                    curFindCount++;
                    c3d.warn(`获取引导节点：${nodeName}失败，使用c3d.find查找失败，将过${delayTime}s后继续查找节点，剩余查找次数：${lastCount}，引导：${this._taskStep.name}`);
                }
                //临时关闭引导遮罩
                this._finishClear(true, true);
                this.notTouch.active = false;
                // await Tool.promise.delay(delayTime);
            }
        } while (!targetNode);

        if (!targetNode.active) {
            //指定节点没有开启，监听等待引导节点激活和开始引导
            await new Promise<void>(resolve => {
                c3d.log(`[指定节点:${targetNode.name}没有开启，监听等待引导节点激活和开始引导]`);
                targetNode.once(`active-in-hierarchy-changed`, () => {
                    c3d.log(`[指定节点:${targetNode.name}开启, 继续进行引导]`);
                    resolve();
                });
            });
        }

        //开启遮罩,引导
        this._finishClear(false, false);
        this.notTouch.active = true;
        //赋值新的查找到的目标节点
        this._targetNode = targetNode;
        //遮罩聚焦到节点目标
        await this._focusToNode(targetNode);
        //手指移动到节点目标
        await this.fingerToNode(targetNode);

        //播放气泡文本动画
        let bubble = this._nodeMap.get(nodeName).registerTextNode;
        // if (bubble) {
        //     let bubbleIsRight = this._taskStep.bubbleIsRight ?? this._taskFile.bubbleIsRight ?? true;
        //     let bubbleIsUp = this._taskStep.bubbleIsUp ?? this._taskFile.bubbleIsUp ?? true;
        //     this.setGuideText(bubble, commandData.text ?? "", bubbleIsRight, bubbleIsUp);
        //     await Tool.comTl.asyncPlayAnim(this.guideTextRoot.getComponent(c3d.Animation), `气泡引导淡入`);
        // }
        //开启穿透
        this.notTouch.active = false;
        //等待点击
        await this.click();
        //关闭穿透，效果，手指
        this.notTouch.active = true;
        this.guideTx.active = false;
        this.guideFinger.active = false;
        // if (bubble)
            // await Tool.comTl.asyncPlayAnim(this.guideTextRoot.getComponent(c3d.Animation), `气泡引导淡出`);
    }

    /**触发的点击区域 */
    async click() {
        //执行手指移动到目标的回调
        await this._taskStep?.onStepExcute?.();

        //等待点击
        let targetNode = this._targetNode;
        let isAuto = this._taskStep.autoRun ?? this._taskFile.autoRun ?? false;
        if (isAuto) {
            await this.touchSimulation(targetNode);
        } else {
            await new Promise<void>(resolve => {
                c3d.log(`等待点击`);
                targetNode.once(c3d.Node.EventType.TOUCH_END, () => {
                    c3d.log("点击目标节点成功");
                    resolve();
                });
                //注意，如果注册的节点是按钮，按钮将会被穿透，
                this.setSwallowTouches(targetNode, false);
            });
        }
    }

    /**************************************************** */

    //******************工具集函数********************* */

    /**
     * 移动手指到目标节点
     * @param node 
     * @param markonCb 
     */
    async fingerToNode(node: c3d.Node) {
        if (!this.guideLayer) {
            return;
        }
        // this.guideFinger.active = true;
        // this.guideFinger.children[0].stopAllActions();

        // //手指反转
        // let fingerDeflectionX = this._taskStep.fingerDeflectionX ? -1 : 1;
        // let fingerDeflectionY = this._taskStep.fingerDeflectionY ? -1 : 1;
        // this.guideFinger.scaleX = Math.abs(this.guideFinger.scaleX) * fingerDeflectionX;
        // this.guideFinger.scaleY = Math.abs(this.guideFinger.scaleY) * fingerDeflectionY;

        // //手指移动
        // let targetPos = v2s.converNodeToOtherNodeSpaceAR(node, this.node).c3dV3;
        // let tipRange = this.guideTx.getComponent(c3d.Animation);
        // this.guideTx.setPosition(targetPos);
        // await GM.anim.moveTo(this.guideFinger, targetPos, 0.3);

        // //播放手指点击的动画
        // this.guideFinger.children[0].getComponent(c3d.Animation).play();
        // //开启点击效果,点击圈效果 
        // this.guideTx.active = true;
        // tipRange.play();
    }

    /**
     * 聚焦到目标节点并绘制图形
     * @param node 查找的节点
     */
    _focusToNode(node: c3d.Node) {
        // let p = v2s.converNodeToOtherNodeSpaceAR(node, this.mask.node).c3dV2;

        // let maskFillet = this._taskStep.maskFillet ?? this._taskFile.maskFillet ?? 0;
        // let maskVirtualization = this._taskStep.maskVirtualization ?? this._taskFile.maskVirtualization ?? 0;
        // let maskType = this._taskStep.maskType ?? this._taskFile.maskType ?? MaskType.rect;
        // let maskAnimTime = this._taskStep.maskAnimTime ?? this._taskFile.maskAnimTime ?? 0.3;

        // if (this._taskStep.maskAnim ?? this._taskFile.maskAnim ?? false) {
        //     //遮罩移动到目标位置的动画
        //     if (maskType == MaskType.rect)
        //         return this.mask.rectTo(maskAnimTime, p, node.width, node.height, maskFillet, maskVirtualization);
        //     else if (maskType == MaskType.cicular)
        //         return this.mask.circleTo(maskAnimTime, p, maskFillet, maskVirtualization);
        // } else {
        //     if (maskType == MaskType.rect)
        //         return this.mask.rect(p, node.width, node.height, maskFillet, maskVirtualization);
        //     else if (maskType == MaskType.cicular)
        //         return this.mask.circle(p, maskFillet, maskVirtualization);
        // }
    }

    /**
     * 设置引导气泡
     * @param posNode 引导气泡的位置
     * @param text 引导文本
     * @param isRight 是否在右边
     * @param isUp 是否左边
     */
    setGuideText(posNode: c3d.Node, text: string, isRight: boolean, isUp: boolean) {
        // this.guideTextRoot.position = v2s.converNodeToOtherNodeSpaceAR(posNode, this.guideTextRoot).c3dV3;
        // this.guideTextCom.string = text.length <= 8 ? text : Tool.str.insertStr(text, 8, "\n");

        // if (isRight) {
        //     if (this.guideTextCom.node.x < 0)
        //         this.guideTextCom.node.x *= -1;
        //     if (this.popBg.scaleX < 0)
        //         this.popBg.scaleX *= -1;
        // } else {
        //     if (this.guideTextCom.node.x > 0)
        //         this.guideTextCom.node.x *= -1;
        //     if (this.popBg.scaleX > 0)
        //         this.popBg.scaleX *= -1;
        // }

        // if (isUp) {
        //     if (this.guideTextCom.node.y < 0)
        //         this.guideTextCom.node.y *= -1;
        //     if (this.popBg.scaleY < 0)
        //         this.popBg.scaleY *= -1;
        // } else {
        //     if (this.guideTextCom.node.y > 0)
        //         this.guideTextCom.node.y *= -1;
        //     if (this.popBg.scaleY > 0)
        //         this.popBg.scaleY *= -1;
        // }
        // (this.guideTextCom as any)._forceUpdateRenderData();
    }

    /**
     * 查找节点
     * @param value 
     * @param cb 
     */
    private async find(value: string) {
        let root = c3d.find('Canvas');
        let node = await this.locateNode(root, value);
        if (!node) {
            c3d.warn("查找节点失败", value);
            return;
        }
        c3d.log("查找节点成功", value);
        return node;
    }

    /**
    * 路径特殊字符使用正则表达式进行拆分
    * @param locator 查询的路径配置，形如：'bottom/bag/bagContent/casting',
    */
    private parse(locator: string) {
        let names = locator.split(/[.,//,>,#]/g);
        let segments = names.map(function (name) {
            let index = locator.indexOf(name);
            let symbol = locator[index - 1] || '/';
            return { symbol: symbol, name: name.trim() };
        });
        return segments;
    }

    /**
    * 根据查找路径和根节点定位要查找的目标节点
    * @param root 
    * @param locatorStr 
    */
    private locateNode(root: c3d.Node, locatorStr: string) {
        return new Promise<c3d.Node>(resolve => {
            let segments = this.parse(locatorStr);
            let child: c3d.Node, node = root;
            for (let i = 0; i < segments.length; i++) {
                let item = segments[i];
                switch (item.symbol) {
                    case '/':
                        child = node.getChildByName(item.name);
                        break;
                }
                if (!child) {
                    node = null;
                    break;
                }
                node = child;
            }
            if (node && node.active) {
                resolve(node);
            } else {
                resolve(null);
            }
        });
    }

    /**
     * 根据html元素获取位置
     * @param element html元素
     * @returns c3d.Rect
     */
    getHTMLElementPosition(element: HTMLElement) {
        var docElem = document.documentElement;
        var leftOffset = window.pageXOffset - docElem.clientLeft;
        var topOffset = window.pageYOffset - docElem.clientTop;
        let left = 0, top = 0, width = 0, height = 0;
        if (typeof element.getBoundingClientRect === 'function') {
            var box = element.getBoundingClientRect();
            left = box.left + leftOffset;
            top = box.top + topOffset;
            width = box.width;
            height = box.height;
        }
        else {
            left = leftOffset;
            top = topOffset;
            if (element instanceof HTMLCanvasElement) {
                width = element.width;
                height = element.height;
            }
            else {
                width = parseInt(element.style.width);
                height = parseInt(element.style.height);
            }
        }
        return new c3d.Rect(left, top, width, height);
    }

    /**
     * 模拟自动点击
     * @param node 要模拟点击的节点
     */
    touchSimulation(node: c3d.Node) {
        c3d.log('自动执行，模拟触摸');
        // return new Promise<void>(resolve => {
        //     this.scheduleOnce(() => {
        //         let p = node.parent.convertToWorldSpaceAR(node.position);
        //         //c3d.log('自动节点 :', JSON.stringify(node.position));
        //         //c3d.log('世界节点 :', JSON.stringify(p));
        //         let x = p.x, y = p.y;
        //         let rect: c3d.Rect;
        //         // @ts-ignore
        //         let inputManager = window._c3d ? window._c3d.inputManager : c3d.internal.inputManager;
        //         if (c3d.sys.isBrowser) {
        //             rect = this.getHTMLElementPosition(document.getElementById("GameCanvas"));
        //         } else {
        //             let frSize = c3d.view.getFrameSize();
        //             rect = new c3d.Rect(0, 0, frSize.width, frSize.height);
        //         }

        //         let vp = c3d.view.getViewportRect();
        //         let sx = c3d.view.getScaleX();
        //         let sy = c3d.view.getScaleY();
        //         let ratio = c3d.view.getDevicePixelRatio();
        //         let htmlx = (x * sx + vp.x) / ratio + rect.x;
        //         let htmly = rect.y + rect.height - (y * sy + vp.y) / ratio;
        //         let pt = c3d.v2(htmlx, htmly);

        //         c3d.log(`模拟点击坐标：${pt.x}, ${pt.y}`);
        //         let touch = inputManager.getTouchByXY(pt.x, pt.y, rect);
        //         inputManager.handleTouchesBegin([touch]);
        //         //模拟点击动画
        //         let btn = node.getComponent(c3d.Button);
        //         if (btn) {
        //             btn[`_pressed`] = true;
        //             btn[`_updateState`]();
        //             btn.clickEvents.forEach(e => e.emit([touch]));
        //         }
        //         setTimeout(() => {
        //             inputManager.handleTouchesEnd([touch]);
        //             if (btn) {
        //                 btn[`_pressed`] = false;
        //                 btn[`_updateState`]();
        //             }
        //             resolve();
        //         }, 300);
        //     }, this._taskStep?.autoClickTime ?? 1);
        // });
    }

    /**
     * 是否为引导层节点
     * @param {*} node 
     */
    isGuideNode(node: c3d.Node) {
        let result = false;
        let temp = node;
        do {
            if (temp === this.node) {
                result = true;
                break;
            }
        } while (temp = temp.parent)
        return result;
    }



    fillPolygon(points: c3d.Rect[]) {
        let p0 = points[0];
        this.getMaskGraphics?.moveTo(p0.x, p0.y);
        points.slice(1).forEach(p => {
            this.getMaskGraphics?.lineTo(p.x, p.y);
        });
        this.getMaskGraphics?.lineTo(p0.x, p0.y);
        this.getMaskGraphics?.stroke();
        this.getMaskGraphics?.fill();
    }

    getRectRotatePoints(rect: c3d.Rect, angle: number, pt: c3d.Vec2) {
        let array = [
            c3d.v2(rect.x, rect.y),
            c3d.v2(rect.x + rect.width, rect.y),
            c3d.v2(rect.x + rect.width, rect.y + rect.height),
            c3d.v2(rect.x, rect.y + rect.height),
        ];
        return array.map(v2 => {
            // let out = c3d.v2();
            // let radian = -angle * RADIAN;
            // out.x = (v2.x - pt.x) * Math.cos(radian) - (v2.y - pt.y) * Math.sin(radian) + pt.x;
            // out.y = (v2.x - pt.x) * Math.sin(radian) + (v2.y - pt.y) * Math.cos(radian) + pt.y;
            // return out;
        });
    }


    /**
     * 获取节点全路径
     * @param {*} node 
     */
    getNodeFullPath(node: c3d.Node) {
        let array = [];
        let temp = node;
        do {
            array.unshift(temp.name);
            temp = temp.parent;
        } while (temp && temp.name !== 'Canvas')
        return array.join('/');
    }

    _dispatchEvent: c3d.Node['dispatchEvent'];
    _recordSteps = [];
    /**
     * 录制节点触摸
     */
    startRecordNodeTouch() {
        if (this.checkIsGuiding(`不能开始录制`)) return;
        if (this._dispatchEvent) {
            c3d.warn('已经进入录制模式');
            return;
        }

        //缓存引擎原生触摸派发函数
        this._dispatchEvent = c3d.Node.prototype.dispatchEvent;
        this._recordSteps = [];

        let self = this;
        let time = Date.now();

        //Hook节点事件派发函数--注入
        c3d.Node.prototype.dispatchEvent = function (event) {
            //执行引擎原生触摸派发函数
            self._dispatchEvent.call(this, event);
            //过滤掉引导节点上的事件，
            if (self.isGuideNode(this)) {
                return;
            }
            //仅缓存对节点的TouchEnd操作
            if (event.type === c3d.Node.EventType.TOUCH_END) {
                let now = Date.now();
                let delay = (now - time) / 1000;
                time = now;
                let args = self.getNodeFullPath(this);
                // self._recordSteps.push({
                //     desc: `点击${args}`,
                //     command: { cmd: 'finger', args },
                //     delay,
                // });
            }
        }
    }

    /**
     * 停止节点触摸录制
     */
    stopRecordNodeTouch() {
        if (this._dispatchEvent) {
            c3d.Node.prototype.dispatchEvent = this._dispatchEvent;
            this._dispatchEvent = null;
            c3d.warn('退出录制状态');
        } else {
            c3d.warn('未进入录制状态');
        }
    }

    /**
     * 回放录制
     */
    playRecordNodeTouch(sender, autoRun) {
        this.stopRecordNodeTouch();
        if (this._recordSteps && this._recordSteps.length) {
            c3d.log('生成任务：', JSON.stringify(this._recordSteps));
            let task = {
                autoRun: !!autoRun,
                debug: true,
                steps: this._recordSteps,
            }
            this._recordSteps = null;
            // this.setTask(task);
            // this.run();
        }
    }


}
